/*
 * Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include <glib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "mobile_provider.h"

const char *testfile = "/tmp/providers_test_data";

typedef struct {
  const char *filename;
  struct mobile_provider_db *provider_db;
} TestFixture;

static char *test_db =
"serviceproviders:2.0\n"
"country:at\n"
"provider:1,2,0,0\n"
"name:,A1/Telekom Austria\n"
"networks:23201\n"
"apn:1,a1.net,ppp@a1plus.at,ppp\n"
"name:,A1 Breitband\n"
"apn:1,aon.data,,ppp\n"
"name:,aon (Flex, Breitband-Duo, BusinessFlex)\n"
"provider:1,3,0,0\n"
"name:,T-Mobile\n"
"networks:23203\n"
"apn:1,gprswap,t-mobile,tm\n"
"name:,WAP\n"
"apn:1,gprsinternet,t-mobile,tm\n"
"name:,Internet\n"
"apn:1,business.gprsinternet,t-mobile,tm\n"
"name:,Business Internet\n"
"country:ca\n"
"provider:1,1,0,0\n"
"name:,Vidéotron\n"
"networks:302500,302510\n"
"apn:1,ihvm.videotron,,\n"
"name:,IHVM\n"
"country:gb\n"
"provider:1,1,0,0\n"
"name:,T-Mobile (Great Britain)\n"
"networks:23430\n"
"apn:0,general.t-mobile.uk,User,mms\n"
"provider:1,1,0,0\n"
"name:,3\n"
"networks:23499\n"
"apn:0,three.uk,,\n"
"country:ie\n"
"provider:2,1,0,1\n"
"name:,Cubic\n"
"name:,CUBIC\n"
"networks:\n"
"apn:0,mtoom.com,mtoom,mtoom\n"
"country:ir\n"
"provider:1,1,0,0\n"
"name:,همراه اول\n"
"networks:43211\n"
"apn:0,mcinet,,\n"
"provider:1,1,0,0\n"
"name:,ایرانسل\n"
"networks:43235\n"
"apn:0,mtnirancell,,\n"
"country:py\n"
"provider:1,2,0,0\n"
"name:,Tigo\n"
"networks:74404\n"
"apn:1,internet.tigo.py,,\n"
"name:,Internet\n"
"apn:2,broadband.tigo.py,tigo,tigo\n"
"name:,Broadband\n"
"name:es,Banda Ancha Móvil\n"
"country:ru\n"
"provider:2,1,0,0\n"
"name:,BaikalWestCom\n"
"name:ru,БайкалВестКом\n"
"networks:25012\n"
"apn:0,inet.bwc.ru,bwc,bwc\n"
"country:us\n"
"provider:1,1,0,0\n"
"name:,3\n"
"networks:31099\n"
"apn:0,three.com,,\n"
"provider:1,4,0,0\n"
"name:,AT&T\n"
"networks:310038,310090,310150,310410,310560,310680\n"
"apn:0,Broadband,,\n"
"apn:1,wap.cingular,,\n"
"name:,MEdia Net\n"
"apn:1,isp.cingular,,\n"
"name:,Data Connect\n"
"apn:1,ISP.CINGULAR,ISPDA@CINGULARGPRS.COM,CINGULAR1\n"
"name:,Data Connect (Accelerated)\n"
"provider:1,4,1,0\n"
"name:,T-Mobile\n"
"networks:310160,310200,310210,310220,310230,310240,310250,310260,310270,"
         "310310,310490,31058,310660,310800\n"
"apn:1,epc.tmobile.com,,\n"
"name:,Internet/WebConnect\n"
"apn:1,wap.voicestream.com,,\n"
"name:,Web2Go/t-zones\n"
"apn:1,internet2.voicestream.com,,\n"
"name:,Internet (old)\n"
"apn:1,internet3.voicestream.com,,\n"
"name:,Internet with VPN (old)\n"
"provider:1,1,0,0\n"
"name:,T-Mobile MVNO\n"
"networks:310160,310200,310210,310220,310230,310240,310250,310260,310270,"
         "310310,310490,31058,310660,310800\n"
"apn:1,mvno.tmobile.com,,\n"
"name:,MVNO Internet/WebConnect\n"
;

static char *test_db_ambiguous =
"serviceproviders:2.0\n"
"country:gb\n"
"provider:1,1,0,0\n"
"name:,T-Mobile (Great Britain)\n"
"networks:23430\n"
"apn:0,general.t-mobile.uk,User,mms\n"
"provider:1,1,0,0\n"
"name:,3\n"
"networks:23499\n"
"apn:0,three.uk,,\n"
"country:ie\n"
"provider:2,1,0,1\n"
"name:,Cubic\n"
"name:,CUBIC\n"
"networks:\n"
"apn:0,mtoom.com,mtoom,mtoom\n"
"country:ir\n"
"provider:1,1,0,0\n"
"name:,همراه اول\n"
"networks:43211\n"
"apn:0,mcinet,,\n"
"provider:1,1,0,0\n"
"name:,ایرانسل\n"
"networks:43235\n"
"apn:0,mtnirancell,,\n"
"country:py\n"
"provider:2,1,0,1\n"
"name:,Cubic\n"
"name:,CUBIC\n"
"networks:\n"
"apn:0,moot.com,moot,moot\n"
"provider:1,2,0,0\n"
"name:,Tigo\n"
"networks:74404\n"
"apn:1,internet.tigo.py,,\n"
"name:,Internet\n"
"apn:2,broadband.tigo.py,tigo,tigo\n"
"name:,Broadband\n"
"name:es,Banda Ancha Móvil\n"
"country:ru\n"
"provider:2,1,0,0\n"
"name:,BaikalWestCom\n"
"name:ru,БайкалВестКом\n"
"networks:25012\n"
"apn:0,inet.bwc.ru,bwc,bwc\n"
;

extern struct mobile_provider *network_find_provider(
    const struct mobile_provider_db *db,
    const gchar *network_id,
    gboolean primary);

void setup_data(const char *file, const char *test_data)
{
  gboolean success = g_file_set_contents(file, test_data, -1, NULL);
  g_assert(success == TRUE);
}

void test_fixture_setup(TestFixture *fixture, gconstpointer test_data)
{
  g_assert(fixture->provider_db == NULL);
  if (fixture->filename == NULL) {
    setup_data(testfile, test_data);
    fixture->filename = testfile;
  }
  fixture->provider_db = mobile_provider_open_db(testfile);
  g_assert(fixture->provider_db != NULL);
}

void test_fixture_teardown(TestFixture *fixture, gconstpointer test_data)
{
  g_assert(fixture->provider_db != NULL);
  mobile_provider_close_db(fixture->provider_db);
  fixture->provider_db = NULL;
}

void test_lookup_by_name(TestFixture *fixture, gconstpointer test_data)
{
  struct mobile_provider *provider;

  provider = mobile_provider_lookup_by_name(fixture->provider_db, "AT&T");
  g_assert(provider != NULL);
  g_assert_cmpint(provider->num_names, ==, 1);
  g_assert_cmpstr(provider->names[0]->name, ==, "AT&T");
  g_assert_cmpstr(provider->country, ==, "us");
  g_assert_cmpint(provider->num_apns, ==, 4);
  g_assert_cmpint(provider->requires_roaming, ==, 0);
  g_assert_cmpint(g_strv_length(provider->networks), ==, 6);
  g_assert_cmpstr(provider->networks[2], ==, "310150");
  g_assert_cmpstr(provider->apns[3]->value, ==, "ISP.CINGULAR");
  g_assert_cmpstr(provider->apns[3]->username, ==, "ISPDA@CINGULARGPRS.COM");
  g_assert_cmpstr(provider->apns[3]->password, ==, "CINGULAR1");
}

void test_lookup_by_mvno(TestFixture *fixture, gconstpointer test_data)
{
  struct mobile_provider *provider;

  provider = mobile_provider_lookup_by_name(fixture->provider_db,
                                            "T-Mobile MVNO");
  g_assert(provider != NULL);
  g_assert_cmpint(provider->primary, ==, 0);
  g_assert_cmpint(provider->num_names, ==, 1);
  g_assert_cmpstr(provider->names[0]->name, ==, "T-Mobile MVNO");
  g_assert_cmpstr(provider->country, ==, "us");
  g_assert_cmpint(provider->num_apns, ==, 1);
  g_assert_cmpint(g_strv_length(provider->networks), ==, 14);
  g_assert_cmpstr(provider->apns[0]->value, ==, "mvno.tmobile.com");
  g_assert(provider->apns[0]->username == NULL);
  g_assert(provider->apns[0]->password == NULL);
}

void test_lookup_by_network(TestFixture *fixture, gconstpointer test_data)
{
  struct mobile_provider *provider;

  provider = mobile_provider_lookup_by_network(fixture->provider_db, "31026");
  g_assert(provider != NULL);
  g_assert_cmpint(provider->primary, ==, 1);
  g_assert_cmpint(provider->num_names, ==, 1);
  g_assert_cmpstr(provider->names[0]->name, ==, "T-Mobile");
  g_assert_cmpstr(provider->country, ==, "us");
  g_assert_cmpint(provider->num_apns, ==, 4);
  g_assert_cmpint(provider->requires_roaming, ==, 0);
  g_assert_cmpint(g_strv_length(provider->networks), ==, 14);
  g_assert_cmpstr(provider->apns[0]->value, ==, "epc.tmobile.com");
  g_assert_cmpstr(provider->apns[1]->value, ==, "wap.voicestream.com");
  g_assert(provider->apns[0]->username == NULL);
  g_assert(provider->apns[1]->password == NULL);
}

void test_other_network_id(TestFixture *fixture, gconstpointer test_data)
{
  struct mobile_provider *provider;
  struct mobile_provider *provider1;

  provider = mobile_provider_lookup_by_network(fixture->provider_db, "310260");
  g_assert(provider != NULL);
  provider1 = mobile_provider_lookup_by_network(fixture->provider_db, "310210");
  g_assert(provider1 == provider);
}

void test_normalize_network_id(TestFixture *fixture, gconstpointer test_data)
{
  struct mobile_provider *provider;
  struct mobile_provider *provider1;

  provider = mobile_provider_lookup_by_network(fixture->provider_db, "31058");
  g_assert(provider != NULL);
  provider1 = mobile_provider_lookup_by_network(fixture->provider_db, "310580");
  g_assert(provider1 == provider);
}

void test_no_network_id(TestFixture *fixture, gconstpointer test_data)
{
  struct mobile_provider *provider;

  /* In the following, the network ID argument will be ignored */
  provider = mobile_provider_lookup_best_match(fixture->provider_db,
                                               "Cubic", "11111");
  g_assert(provider != NULL);
  g_assert(provider->next == NULL);
  g_assert_cmpstr(provider->country, ==, "ie");
  g_assert_cmpint(provider->num_names, ==, 2);
  g_assert_cmpint(provider->num_apns, ==, 1);
  g_assert_cmpint(provider->requires_roaming, ==, 1);
}

void test_no_network_id_ambiguous(TestFixture *fixture,
                                  gconstpointer test_data)
{
  struct mobile_provider *provider;

  /* In the following, the network ID argument will be ignored */
  provider = mobile_provider_lookup_best_match(fixture->provider_db,
                                               "Cubic", "11111");
  g_assert(provider == NULL);
}

void test_multi_country(TestFixture *fixture, gconstpointer test_data)
{
  struct mobile_provider *provider;

  provider = mobile_provider_lookup_by_name(fixture->provider_db, "T-Mobile");
  g_assert(provider != NULL);
  g_assert_cmpstr(provider->country, ==, "at");
  g_assert(provider->next != NULL);
  g_assert_cmpstr(provider->next->country, ==, "gb");
  g_assert(provider->next->next != NULL);
  g_assert_cmpstr(provider->next->next->country, ==, "us");
}

void test_non_ascii_names(TestFixture *fixture, gconstpointer test_data)
{
  struct mobile_provider *provider;

  provider = mobile_provider_lookup_by_name(fixture->provider_db, "Vidéotron");
  g_assert(provider != NULL);
  g_assert_cmpint(provider->num_names, ==, 1);
  g_assert_cmpstr(provider->names[0]->name, ==, "Vidéotron");
  g_assert_cmpstr(provider->country, ==, "ca");

  provider = mobile_provider_lookup_by_name(fixture->provider_db,
                                            "همراه اول");
  g_assert(provider != NULL);
  g_assert_cmpint(provider->num_names, ==, 1);
  g_assert_cmpstr(provider->names[0]->name, ==, "همراه اول");
  g_assert_cmpstr(provider->country, ==, "ir");
}

void test_multi_provider_names(TestFixture *fixture, gconstpointer test_data)
{
  struct mobile_provider *provider;

  provider = mobile_provider_lookup_by_name(fixture->provider_db,
                                            "BaikalWestCom");
  g_assert(provider != NULL);
  g_assert_cmpstr(provider->country, ==, "ru");
  g_assert_cmpint(provider->num_names, ==, 2);
  g_assert_cmpstr(provider->names[0]->name, ==, "BaikalWestCom");
  g_assert(provider->names[0]->lang == NULL);
  g_assert_cmpstr(provider->names[1]->name, ==, "БайкалВестКом");
  g_assert(provider->names[1]->lang != NULL);
  g_assert_cmpstr(provider->names[1]->lang, ==, "ru");

  provider = mobile_provider_lookup_by_name(fixture->provider_db,
                                            "БайкалВестКом");
  g_assert(provider != NULL);
  g_assert_cmpstr(provider->country, ==, "ru");
  g_assert_cmpint(provider->num_names, ==, 2);
  g_assert_cmpstr(provider->names[0]->name, ==, "BaikalWestCom");
}

void test_multi_apn_names(TestFixture *fixture, gconstpointer test_data)
{
  struct mobile_provider *provider;

  provider = mobile_provider_lookup_by_name(fixture->provider_db, "Tigo");
  g_assert(provider != NULL);
  g_assert_cmpstr(provider->country, ==, "py");
  g_assert_cmpint(provider->num_names, ==, 1);
  g_assert_cmpstr(provider->names[0]->name, ==, "Tigo");
  g_assert(provider->names[0]->lang == NULL);
  g_assert_cmpint(provider->num_apns, ==, 2);

  g_assert_cmpint(provider->apns[0]->num_names, ==, 1);
  g_assert_cmpstr(provider->apns[0]->value, ==, "internet.tigo.py");
  g_assert_cmpstr(provider->apns[0]->names[0]->name, ==, "Internet");
  g_assert(provider->apns[0]->names[0]->lang == NULL);

  g_assert_cmpint(provider->apns[1]->num_names, ==, 2);
  g_assert_cmpstr(provider->apns[1]->value, ==, "broadband.tigo.py");
  g_assert_cmpstr(provider->apns[1]->names[0]->name, ==, "Broadband");
  g_assert(provider->apns[1]->names[0]->lang == NULL);
  g_assert_cmpstr(provider->apns[1]->names[1]->name, ==, "Banda Ancha Móvil");
  g_assert(provider->apns[1]->names[1]->lang != NULL);
  g_assert_cmpstr(provider->apns[1]->names[1]->lang, ==, "es");
}

void test_name_with_commas(TestFixture *fixture, gconstpointer test_data)
{
  struct mobile_provider *provider;

  provider = mobile_provider_lookup_by_network(fixture->provider_db, "23201");
  g_assert(provider != NULL);
  g_assert_cmpint(provider->num_names, ==, 1);
  g_assert_cmpstr(provider->names[0]->name, ==, "A1/Telekom Austria");
  g_assert_cmpint(provider->num_apns, ==, 2);
  g_assert_cmpint(provider->apns[1]->num_names, ==, 1);
  g_assert_cmpstr(provider->apns[1]->names[0]->name, ==,
                  "aon (Flex, Breitband-Duo, BusinessFlex)");
}

void test_bad_provider_name(TestFixture *fixture, gconstpointer test_data)
{
  struct mobile_provider *provider;

  provider = mobile_provider_lookup_by_name(fixture->provider_db,
                                            "NOSUCHPROVIDER");
  g_assert(provider == NULL);
}

void test_bad_network_id(TestFixture *fixture, gconstpointer test_data)
{
  struct mobile_provider *provider;

  provider = mobile_provider_lookup_by_network(fixture->provider_db,
                                               "000000");
  g_assert(provider == NULL);
}

void test_lookup_best_match(TestFixture *fixture, gconstpointer test_data)
{
  struct mobile_provider *provider;

  /* Make sure we find the one in the US */
  provider = mobile_provider_lookup_best_match(fixture->provider_db, "3",
                                               "31099");
  g_assert_cmpint(provider->num_apns, ==, 1);
  g_assert_cmpstr(provider->apns[0]->value, ==, "three.com");

  /* Make sure we find the one in the UK */
  provider = mobile_provider_lookup_best_match(fixture->provider_db, "3",
                                               "23499");
  g_assert_cmpint(provider->num_apns, ==, 1);
  g_assert_cmpstr(provider->apns[0]->value, ==, "three.uk");

  /* Make sure we find the one in the UK even with a different MNC */
  provider = mobile_provider_lookup_best_match(fixture->provider_db, "3",
                                               "234878");
  g_assert_cmpint(provider->num_apns, ==, 1);
  g_assert_cmpstr(provider->apns[0]->value, ==, "three.uk");

  /* test for NULL db */
  provider = mobile_provider_lookup_best_match(NULL, "3", "234878");

  g_assert(provider == NULL);
}

void test_find_provider(TestFixture *fixture, gconstpointer test_data)
{
  struct mobile_provider *provider;

  /* test for NULL db */
  provider = network_find_provider(NULL, "234878", 0);

  g_assert(provider == NULL);

  /* test for NULL network id */
  provider = network_find_provider(fixture->provider_db, NULL, 0);

  g_assert(provider == NULL);
}

void test_get_name (TestFixture *fixture, gconstpointer test_data)
{
  const gchar* name;

  /* test for NULL provider */
  name = mobile_provider_get_name(NULL);

  g_assert(name == NULL);
}

int main(int argc, char *argv[])
{
  int result;

  g_test_init(&argc, &argv, NULL);

#define ADD_TEST(name, data) \
  g_test_add("/flimflam/providerdb/" #name, TestFixture, data, \
             test_fixture_setup, test_##name, test_fixture_teardown)

  ADD_TEST(lookup_by_name, test_db);
  ADD_TEST(lookup_by_network, test_db);
  ADD_TEST(lookup_by_mvno, test_db);
  ADD_TEST(other_network_id, test_db);
  ADD_TEST(normalize_network_id, test_db);
  ADD_TEST(no_network_id, test_db);
  ADD_TEST(no_network_id_ambiguous, test_db_ambiguous);
  ADD_TEST(multi_country, test_db);
  ADD_TEST(non_ascii_names, test_db);
  ADD_TEST(multi_provider_names, test_db);
  ADD_TEST(multi_apn_names, test_db);
  ADD_TEST(name_with_commas, test_db);
  ADD_TEST(bad_provider_name, test_db);
  ADD_TEST(bad_network_id, test_db);
  ADD_TEST(lookup_best_match, test_db);
  ADD_TEST(find_provider, test_db);
  ADD_TEST(get_name, test_db);
  ADD_TEST(find_provider, test_db);

  result = g_test_run();

  unlink(testfile);
  return result;
}
